package cn.openread;

import javassist.*;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;

/**
 * 用途：一切为了业务
 * <p>
 * Created by simon on 2020/3/12.
 */
public class Agent {

    public static void premain(String args, final Instrumentation instrumentation) throws UnmodifiableClassException, IOException, ClassNotFoundException {
        System.out.println("x-premain：" + args);

//        final Person person = new Person("Mary");

        instrumentation.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader,
                                    String className,
                                    Class<?> classBeingRedefined,
                                    ProtectionDomain protectionDomain,
                                    byte[] classfileBuffer) throws IllegalClassFormatException {
                try {
                    if (!"cn/openread/Person".equals(className)) {
                        return null;
                    }
                    return buildMoitor();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, true);
//        instrumentation.retransformClasses(Person.class);
    }

    public static byte[] buildMoitor() throws IOException {
        try {
            //1.copy一个新的
            ClassPool pool = new ClassPool();
            pool.appendSystemPath();

            CtClass ctclass = pool.get("cn.openread.Person");
            CtMethod ctMethod = ctclass.getDeclaredMethod("say");
            CtMethod copy = CtNewMethod.copy(ctMethod, ctclass, new ClassMap());
            //2.旧的进行重命名
            ctMethod.setName("say$agent");
            //3.加个新的进行监听
            copy.setBody("{" +
                    "                Long start = System.nanoTime();" +
                    "                System.out.println(start);" +
                    "                try {" +
                    "                    return say$agent($$);" +
                    "                } finally {" +
                    "                    System.out.println(System.nanoTime());" +
                    "                }" +
                    "            }");

            ctclass.addMethod(copy);
            return ctclass.toBytecode();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }
        return new byte[0];
    }

//    InputStream stream = Person.class.getResourceAsStream("/Person.class");
//    byte[] bytes = IOUtils.readFully(stream, -1, false);
//        instrumentation.redefineClasses(new ClassDefinition(Person.class, bytes));
//        person.say("xxxx");


//    public void back(){
//        System.out.println("x-premain：" + args);
//
//        Person person = new Person("Mary");
//
//        instrumentation.addTransformer(new ClassFileTransformer() {
//            public byte[] transform(ClassLoader loader,
//                                    String className,
//                                    Class<?> classBeingRedefined,
//                                    ProtectionDomain protectionDomain,
//                                    byte[] classfileBuffer) throws IllegalClassFormatException {
//                try {
//                    if (!"cn/openread/Person".equals(className)) {
//                        return null;
//                    }
//                    InputStream stream = Agent.class.getResourceAsStream("/Person.class");
//                    return IOUtils.readFully(stream, -1, false);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//                return null;
//            }
//        }, true);
//        instrumentation.retransformClasses(Person.class);
//    }
}
